import UIKit

//下标语法
//下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似，定义下标使用 subscript 关键字，指定一个或多个输入参数和返回类型；与实例方法不同的是，下标可以设定为读写或只读。这种行为由 getter 和 setter 实现，有点类似计算型属性：
//如同只读计算型属性，可以省略只读下标的 get 关键字：
struct TimesTable {
    let multipLier: Int
    subscript(index: Int) -> Int {
        return multipLier * index
    }
    
    //打印当前表
    func showTable() {
        for i in 1..<multipLier+1 {
            print("\(i) * \(multipLier) = \(self[i])   ",terminator:"")
        }
        print("")
    }
}
let threeTimesTable = TimesTable(multipLier: 3)
print("six times three is \(threeTimesTable[6])")
//打印乘法表
for i in 1..<10 {
    let timesTable = TimesTable(multipLier: i)
    timesTable.showTable()
}

//下标选项
//下标可以接受任意数量的入参，并且这些入参可以是任意类型。下标的返回值也可以是任意类型。下标可以使用可变参数，并且可以提供默认参数数值，但是不能使用输入输出参数。
//
//一个类或结构体可以根据自身需要提供多个下标实现，使用下标时将通过入参的数量和类型进行区分，自动匹配合适的下标，这就是下标的重载。
//
//虽然接受单一入参的下标是最常见的，但也可以根据情况定义接受多个入参的下标。例如下例定义了一个 Matrix 结构体，用于表示一个 Double 类型的二维矩阵。Matrix 结构体的下标接受两个整型参数：
struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows:Int ,columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row:Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column),"Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column),"Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0,1] = 1.5
matrix[1,0] = 3.2
print(matrix[1,1])
print(matrix[1,0])
print(matrix.grid)

let someValue = matrix[1,1]

//类型下标
//正如上节所述，实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型本身上调用的下标。这种下标的类型被称作类型下标。你可以通过在 subscript 关键字之前写下 static 关键字的方式来表示一个类型下标。类可以使用 class 关键字来允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标：
enum Planet: Int {
    case mercury = 1,
         venus,
         earth,
         mars,
         jupiter,
         saturn,
         uranus,
         neptune
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}

let mars = Planet[4]
print(mars)
